# from appium.webdriver.common.touch_action import TouchAction
# from selenium.webdriver import ActionChains
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec
from selenium.common.exceptions import NoSuchElementException, TimeoutException, StaleElementReferenceException
from time import sleep
from functools import partial

from util.user_log import log
from util.base_util import MyError, BaseUtil
from base import config

is_ele, rerun = BaseUtil.is_ele, BaseUtil.rerun
wait_time = config.webDriver_wait


class FindElement:
    def __init__(self, driver):
        self.driver = driver

    def find_toast(self, element_data, frequency=0.5):
        return self.find_element_all(element_data, wait_type='presence', frequency=frequency)

    def find_element_all(self, element_data, timeout=wait_time, frequency=0.5, wait_type='visibility'):
        if isinstance(element_data, tuple):
            tulpe_01 = element_data
            len_list = len(tulpe_01)
            if len_list == 2:
                return self.__find_element(tulpe_01, close_browser_when_failed=False,
                                           timeout=timeout, frequency=frequency, wait_type=wait_type), element_data
            elif len_list == 3:
                list_0, index, list_2 = tulpe_01
                try:
                    index = int(index)
                except:
                    index = None
                tuple01 = list_0, list_2
                ele = self.__find_elements(tuple01, index=index, close_browser_when_failed=False,
                                           timeout=timeout, frequency=frequency, wait_type=wait_type)
                if isinstance(ele, list):
                    is01 = '是'
                    list_ele = [tuple([i, element_data]) for i in ele]
                    log.debug('元素{}一个列表, {}'.format(is01, list_ele))
                    return list_ele
                return ele, element_data
        else:
            type_param = type(element_data)
            raise MyError('格式错误：入参格式必须是2个或3个值的元祖'
                          '\n参数格式, 与参数的值为: {}，{}'.format(type_param, element_data))

    def __find_element(self, locator, timeout, frequency, element=None,
                       wait_type='visibility', close_browser_when_failed=False):
        """
        :param locator: 定位信息方式，数据类型是元组,例如(By.ID,"id_value")
        :param element: 默认值为None，
                如果有值，就是页面元素，将会在这个元素之上发现它的子元素（二次定位）
        :return:返回定位的元素
        """
        if element:
            return self.wait(timeout, frequency=frequency).until(ec.visibility_of(element.find_element(locator)))
        try:
            if wait_type == 'visibility':
                return self.wait(timeout, frequency=frequency).until(ec.visibility_of_element_located(locator))
            elif wait_type == 'presence':
                return self.wait(timeout, frequency=frequency).until(ec.presence_of_element_located(locator))
        except TimeoutException as err:
            if close_browser_when_failed:
                self.driver.quit()
            log.error("找不到元素{}, 等待时间：{}秒".format(locator, timeout))
            raise TimeoutException(msg="\n找不到元素{}, 等待时间：{}秒".format(locator, timeout))
        except NoSuchElementException:
            if close_browser_when_failed:
                self.driver.quit()
            log.error('找不到元素，元素：{}'.format(locator))
            raise NoSuchElementException(msg='\n找不到元素，元素：{}'.format(locator))

    def __find_elements(self, locator, index=None, element=None, timeout=wait_time,
                        wait_type="visibility", close_browser_when_failed=False, frequency=0.5):

        """
        发现很多元素方法
        :param locator:定位信息方式和值，支持的是元组类型。例如(By.ID,"id_value")
        :param element: 默认值为None，
                如果有值，那么这个值是一个页面元素，这个方法将会在这个元素之上发现它的子元素
        :param timeout: 默认值为None，但是为None时，将会取配置文件中的超时时间配置
        :param wait_type: 等待的类型，支持两种等待方式，一种是可见等待visibility，另外一种是存在等待presence
        :param when_failed_close_browser: 当定位信息失败时，浏览器是否关闭
        :return: 返回定位的元素们
        """
        # print(locator);print(type(locator))
        # log.debug('元素：locator=%s, index=%s' % (locator, index))
        if element:
            return element.find_elements(locator), locator
        try:
            # 存在不一定可见，可见才能操作
            if wait_type == "visibility":
                ele = self.wait(timeout, frequency=frequency).until(
                    ec.visibility_of_all_elements_located(locator=locator))
                log.debug('该组元素为：%s' % ele)
                if index is not None:
                    # log.debug('指定了index：%s；返回单个元素' % index)
                    ele = ele[index]
                return ele
            else:
                log.debug('没有指定index：%s；返回一组元素' % index)
                ele_s = self.wait(timeout, frequency=frequency).until(
                    ec.presence_of_all_elements_located(locator=locator))
                log.debug('该组元素为：%s' % ele_s)
                log.debug('该组元素长度为：%s' % len(ele_s))
                return ele_s
        except TimeoutException:
            if close_browser_when_failed:
                self.driver.quit()
            log.error("找不到元素{}, 等待时间：{}秒".format(locator, timeout))
            raise TimeoutException(msg="\n找不到元素{}, 等待时间：{}秒".format(locator, timeout))
        except NoSuchElementException:
            if close_browser_when_failed:
                self.driver.quit()
            raise NoSuchElementException(msg='\n找不到元素，元素：{}'.format(locator))

    def is_element(self, element):
        one = element[0]
        if isinstance(one, str):
            return self.find_element_all(element)
        elif isinstance(one, WebElement):
            return element
        else:
            raise Exception('元素信息格式错误：既不是str也不是WebElement')

    def until_ele_disappear(self, element_data, timeout=0):
        try:
            num = 0
            while True:
                num += 1
                self.find_element_all(element_data, timeout=timeout)
                log.debug('检测弹窗元素是否消失(第{}次)'.format(num))
                sleep(0.1)
                if num == 10:
                    break
        except:
            log.debug('弹窗元素已消失')
            sleep(0.1)
            return True

    def wait(self, timeout, frequency):
        return WebDriverWait(driver=self.driver, timeout=timeout, poll_frequency=frequency)


class ApiSelenium(FindElement):
    def __init__(self, driver):
        super().__init__(driver)

    @is_ele
    def clear(self, ele):
        return ele.clear()

    @is_ele
    @rerun
    def send_keys(self, ele, key, key02=None, clear=True):
        if clear:
            ele.clear()
        log.debug('输入的值为：{}'.format(key))
        ele.send_keys(key)
        if key02:
            log.debug('输入的值2为: {}'.format(key02))
            ele.send_keys(key02)
        return

    @is_ele
    @rerun
    def click(self, ele):
        return ele.click()

    def keyevent(self, keycode, metastate=None):
        self.driver.keyevent(keycode, metastate)

    @is_ele
    def get_attribute(self, ele, attribute):
        # attribute = 'name' or 'text' or 'resourceId' or 'className'
        try:
            value = ele.get_attribute(attribute)
            log.debug('attribute为: {};  元素属性的值为：{}'.format(attribute, value))
        except StaleElementReferenceException as err:
            raise MyError('{}|元素过期异常'.format(err))
        else:
            return value


class Base(ApiSelenium):
    def __init__(self, driver):
        super().__init__(driver)

    def assert_run(self, expect, result, way='=='):
        BaseUtil.assert_func(expect, result, way)

    # @classmethod
    def tap(self, positions: tuple):
        """
        坐标点击
        positions = [(100, 20), (100, 60)]
        positions = (100, 20)
        目前的参数格式：([648, 1773], [864, 1920], size01)
        get坐标（平均值），get设备分辨率，-->相对坐标
        get当前设备分辨率， 最后get相对坐标, 转换成int --》当前实际坐标
        """
        print(positions)
        start, end, size01 = positions
        avg_x = (start[0] + end[0]) / 2
        avg_y = (start[1] + end[1]) / 2
        # 获取相对分辨率
        x_, y_ = avg_x / size01[0], avg_y / size01[1]
        # 获取实际分辨率
        size_current = [i for i in self.get_window_size().values()]
        # size_current = size01
        positions = x_ * size_current[0], y_ * size_current[1]
        # 把float转int（不精确的四写五入）
        positions = [tuple(map(round, positions))]
        # return positions
        return self.driver.tap(positions, duration=None)

    def back(self):
        log.debug('【点击返回键】')
        return self.driver.back()

    def swipe_run(self, param, duration=None):
        param_between = ['up', 'down', 'left', 'right']
        if param not in param_between:
            raise MyError('参数错误! 参数必须为以下列表的其中之一:\n{}'.format(param_between))

        def swipe(x1=0.5, y1=0.5, x2=0.5, y2=0.5, duration=None):
            size = self.get_window_size()
            x, y = [size[i] for i in ('width', 'height')]
            self.driver.swipe(x1 * x, y1 * y, x2 * x, y2 * y, duration)
            return x1 * x, y1 * y, x2 * x, y2 * y

        # 上：y，70-30；下：y, 30-70；
        # 左：x, 80-20；右：x, 20-80
        if param == 'up':
            return swipe(y1=0.7, y2=0.3, duration=duration)
        elif param == 'down':
            return swipe(y1=0.3, y2=0.7, duration=duration)
        elif param == 'left':
            return swipe(x1=0.8, x2=0.2, duration=duration)
        elif param == 'right':
            return swipe(x1=0.2, x2=0.8, duration=duration)

    def get_window_size(self):
        return self.driver.get_window_size()

    def get_contexts(self):
        res = self.driver.contexts
        log.debug(f"获取所有句柄: {res}")
        return res

    def switch_to_context(self, value):
        log.debug(f'切换句柄到：{value}')
        return self.driver.switch_to.context(value)

    def current_context(self):
        _return = self.driver.current_context()
        log.debug(F'当前句柄为{_return}')
        return _return


if __name__ == '__main__':
    res = Base('').swipe_run('righ')
    print(res)
